import { Injectable } from '@nestjs/common';
import { Repository, FindManyOptions, FindOneOptions } from 'typeorm';
import { BaseEntity } from './base.entity';
import { PageResult } from '../response/page-result.class';

/**
 * 基础服务
 * 提供通用的业务逻辑抽象
 * 基于PHP和Java的BaseService统一设计
 * 
 * 特点:
 * 1. 使用TypeORM原生Repository
 * 2. 统一分页格式 (与Java PageResult一致)
 * 3. 不处理响应格式 (由Controller层处理)
 * 4. 专注于业务逻辑
 */
@Injectable()
export abstract class BaseService<T extends BaseEntity> {
  constructor(protected readonly repository: Repository<T>) {}

  /**
   * 创建实体
   * @param entity 实体数据
   * @returns Promise<T>
   */
  async create(entity: Partial<T>): Promise<T> {
    const newEntity = this.repository.create(entity as any);
    const result = await this.repository.save(newEntity);
    return Array.isArray(result) ? result[0] : result;
  }

  /**
   * 批量创建
   * @param entities 实体数组
   * @returns Promise<T[]>
   */
  async createMany(entities: Partial<T>[]): Promise<T[]> {
    const newEntities = this.repository.create(entities as any);
    return this.repository.save(newEntities);
  }

  /**
   * 根据ID查找
   * @param id 主键ID
   * @returns Promise<T | null>
   */
  async findById(id: number): Promise<T | null> {
    return this.repository.findOne({ where: { id } } as FindOneOptions<T>);
  }

  /**
   * 查找单个实体
   * @param options 查询选项
   * @returns Promise<T | null>
   */
  async findOne(options: FindOneOptions<T>): Promise<T | null> {
    return this.repository.findOne(options);
  }

  /**
   * 查找多个实体
   * @param options 查询选项
   * @returns Promise<T[]>
   */
  async find(options?: FindManyOptions<T>): Promise<T[]> {
    return this.repository.find(options);
  }

  /**
   * 分页查询
   * 返回格式与Java PageResult一致
   * @param page 页码
   * @param limit 每页数量
   * @param options 查询选项
   * @returns Promise<PageResult<T>>
   */
  async paginate(
    page: number = 1,
    limit: number = 15,
    options?: FindManyOptions<T>,
  ): Promise<PageResult<T>> {
    const [data, total] = await this.repository.findAndCount({
      ...options,
      skip: (page - 1) * limit,
      take: limit,
    });

    return new PageResult<T>(page, limit, total, data);
  }

  /**
   * 更新实体
   * @param id 主键ID
   * @param entity 更新数据
   * @returns Promise<T | null>
   */
  async update(id: number, entity: Partial<T>): Promise<T | null> {
    await this.repository.update(id, entity as any);
    return this.findById(id);
  }

  /**
   * 软删除
   * @param id 主键ID
   * @returns Promise<boolean>
   */
  async delete(id: number): Promise<boolean> {
    const result = await this.repository.softDelete(id);
    return (result.affected || 0) > 0;
  }

  /**
   * 硬删除
   * @param id 主键ID
   * @returns Promise<boolean>
   */
  async hardDelete(id: number): Promise<boolean> {
    const result = await this.repository.delete(id);
    return (result.affected || 0) > 0;
  }

  /**
   * 批量软删除
   * @param ids 主键ID数组
   * @returns Promise<boolean>
   */
  async deleteMany(ids: number[]): Promise<boolean> {
    const result = await this.repository.softDelete(ids);
    return (result.affected || 0) > 0;
  }

  /**
   * 统计数量
   * @param options 查询选项
   * @returns Promise<number>
   */
  async count(options?: FindManyOptions<T>): Promise<number> {
    return this.repository.count(options);
  }

  /**
   * 根据条件查找单个实体
   * @param where 查询条件
   * @returns Promise<T | null>
   */
  async findOneBy(where: Partial<T>): Promise<T | null> {
    return this.repository.findOne({ where } as FindOneOptions<T>);
  }

  /**
   * 根据条件查找多个实体
   * @param where 查询条件
   * @returns Promise<T[]>
   */
  async findManyBy(where: Partial<T>): Promise<T[]> {
    return this.repository.find({ where } as FindManyOptions<T>);
  }

  /**
   * 检查实体是否存在
   * @param id 主键ID
   * @returns Promise<boolean>
   */
  async exists(id: number): Promise<boolean> {
    const count = await this.repository.count({ where: { id } } as FindManyOptions<T>);
    return count > 0;
  }

  /**
   * 根据条件检查实体是否存在
   * @param where 查询条件
   * @returns Promise<boolean>
   */
  async existsBy(where: Partial<T>): Promise<boolean> {
    const count = await this.repository.count({ where } as FindManyOptions<T>);
    return count > 0;
  }
}